package com.zim.ta_service.base.redis;

import lombok.Getter;
import org.springframework.data.redis.core.*;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;

import java.time.Duration;
import java.util.Arrays;
import java.util.Collection;
import java.util.function.Supplier;

@Getter
@SuppressWarnings("unchecked")
public class ZimRedis {
    private final RedisTemplate<String, Object> redisTemplate;
    private final ValueOperations<String, Object> valueOps;
    private final HashOperations<String, Object, Object> hashOps;
    private final ListOperations<String, Object> listOps;
    private final SetOperations<String, Object> setOps;
    private final ZSetOperations<String, Object> zSetOps;

    public ZimRedis(RedisTemplate<String, Object> redisTemplate) {
        this.redisTemplate = redisTemplate;
        Assert.notNull(redisTemplate, "redisTemplate is null");

        this.valueOps = redisTemplate.opsForValue();
        this.hashOps = redisTemplate.opsForHash();
        this.listOps = redisTemplate.opsForList();
        this.setOps = redisTemplate.opsForSet();
        this.zSetOps = redisTemplate.opsForZSet();
    }

    /**
     * 存放 key value 对到 redis。
     */
    public void set(String key, Object value) {
        valueOps.set(key, value);
    }

    /**
     * 存放 key value 对到 redis，并将 key 的生存时间设为 seconds (以秒为单位)。
     * 如果 key 已经存在， SETEX 命令将覆写旧值。
     */
    public void setEx(String key, Object value, Duration timeout) {
        valueOps.set(key, value, timeout);
    }


    /**
     * 返回 key 所关联的 value 值
     * 如果 key 不存在那么返回特殊值 nil 。
     */
    @Nullable
    public <T> T get(String key) {
        return (T) valueOps.get(key);
    }

    /**
     * 获取cache 为 null 时使用加载器，然后设置缓存
     *
     * @param key    cacheKey
     * @param loader cache loader
     * @param <T>    泛型
     * @return 结果
     */
    @Nullable
    public <T> T get(String key, Supplier<T> loader) {
        T value = this.get(key);
        if (value != null) {
            return value;
        }
        value = loader.get();
        if (value == null) {
            return null;
        }
        this.set(key, value);
        return value;
    }

    /**
     * 删除给定的多个 key
     * 不存在的 key 会被忽略。
     */
    public Long del(String... keys) {
        return del(Arrays.asList(keys));
    }

    /**
     * 删除给定的多个 key
     * 不存在的 key 会被忽略。
     */
    public Long del(Collection<String> keys) {
        return redisTemplate.delete(keys);
    }

    /**
     * 将 key 中储存的数字值减一。
     * 如果 key 不存在，那么 key 的值会先被初始化为 0 ，然后再执行 DECR 操作。
     * 如果值包含错误的类型，或字符串类型的值不能表示为数字，那么返回一个错误。
     * 本操作的值限制在 64 位(bit)有符号数字表示之内。
     * 关于递增(increment) / 递减(decrement)操作的更多信息，请参见 INCR 命令。
     */
    public Long decr(String key) {
        return valueOps.decrement(key);
    }

    /**
     * 将 key 所储存的值减去减量 decrement 。
     * 如果 key 不存在，那么 key 的值会先被初始化为 0 ，然后再执行 DECRBY 操作。
     * 如果值包含错误的类型，或字符串类型的值不能表示为数字，那么返回一个错误。
     * 本操作的值限制在 64 位(bit)有符号数字表示之内。
     * 关于更多递增(increment) / 递减(decrement)操作的更多信息，请参见 INCR 命令。
     */
    public Long decrBy(String key, long longValue) {
        return valueOps.decrement(key, longValue);
    }

    /**
     * 将 key 中储存的数字值增一。
     * 如果 key 不存在，那么 key 的值会先被初始化为 0 ，然后再执行 INCR 操作。
     * 如果值包含错误的类型，或字符串类型的值不能表示为数字，那么返回一个错误。
     * 本操作的值限制在 64 位(bit)有符号数字表示之内。
     */
    public Long incr(String key) {
        return valueOps.increment(key);
    }

    /**
     * 将 key 所储存的值加上增量 increment 。
     * 如果 key 不存在，那么 key 的值会先被初始化为 0 ，然后再执行 INCRBY 命令。
     * 如果值包含错误的类型，或字符串类型的值不能表示为数字，那么返回一个错误。
     * 本操作的值限制在 64 位(bit)有符号数字表示之内。
     * 关于递增(increment) / 递减(decrement)操作的更多信息，参见 INCR 命令。
     */
    public Long incrBy(String key, long longValue) {
        return valueOps.increment(key, longValue);
    }

    /**
     * 获取记数器的值
     */
    public Long getCounter(String key) {
        return Long.valueOf(String.valueOf(valueOps.get(key)));
    }

    /**
     * 检查给定 key 是否存在。
     */
    public Boolean exists(String key) {
        return redisTemplate.hasKey(key);
    }
}
